Android 13

您所在的位置:网站首页 mediaplayer seekto Android 13

Android 13

2024-07-05 07:08:39| 来源: 网络整理| 查看: 265

全新系列文章已更新:

Android Media Framework - 开篇Android Media Framework(一)OpenMAX 框架简介Android Media Framework(二)OpenMAX 类型阅读与分析Android Media Framework(三)OpenMAX API阅读与分析Android Media Framework(四)Non-Tunneled组件的状态转换与buffer分配过程分析Android Media Framework(五)Tunnel ModeAndroid Media Framework(六)插件式编程与OMXStoreAndroid Media Framework(七)MediaCodecServiceAndroid Media Framework(八)OMXNodeInstance - ⅠAndroid Media Framework(九)OMXNodeInstance - ⅡAndroid Media Framework(十)OMXNodeInstance - Ⅲ

上一节了解了MediaPlayer api的使用,这一节就我们将会了解MediaPlayer的生命周期与api使用细节。

1、MediaPlayer生命周期

MediaPlayer.java 一开始有对生命周期的描述,这里对这些内容进行翻译:

MediaPlayer 是线程不安全的,创建以及调用应该在同一个线程中,如果需要使用Callback,那么线程必须要有一个Looper;调用 new 或者 reset 后MediaPlayer进入 Idle 状态,release 调用后进入到 End 状态,这两个状态之间就是 MediaPlayer 对象的生命周期;调用 new 或者 reset都会进入Idle 状态,但是它们仍然是有区别的。getDuration 等方法在Idle 状态下调用会出现error,如果是new之后调用,虽然会返回error,但是error并不是由内部播放器引擎上抛的,播放器状态仍然保持在Idle 状态;如果刚好是在reset之后调用getDuration方法,那么内部播放器引擎会上抛error事件,并且把播放器状态置为Error;如果MediaPlayer对象不再需要被使用,推荐调用release方法,让内部播放器引擎使用的资源能够被释放;一旦进入了 End 状态,MediaPlayer对象将不能再被使用,并且不能再被切换到其他状态,如果仍要使用需要重新new一个对象,进入Idle状态。有很多原因会造成播放失败,比如不支持的audio/video格式、分辨率过高、streaming超时等,这些错误会让播放器进入Error状态并且上抛事件,如果要继续使用当前MediaPlayer对象,可以调用reset使其进入到Idle状态;setDataSource会让播放器从Idle进入Loaded状态,如果在其他状态调用则会抛出IllegalStateException错误;prepare会让播放器从Loaded状态进入到Prepared状态,如果调用的是prepareAsync,则会先进入中间状态Preparing;在除了Loaded和Stopped状态外调用prepare方法都是非法的,会抛出IllegalStateException错误;start方法会让播放器从Prepared状态进入到Started状态,可以用isPlaying判断播放器当前状态是否在Started状态;如果已经进入到Started状态,则再调用start不会有任何影响;pause方法会让播放器进入到Paused状态,从Started进入到Paused或者反过来的过程都是异步的;如果已经进入到Paused状态,则再调用pause不会有任何影响;在Paused状态下重新调用start将会恢复播放,进入到Started状态;stop会让播放器从Started、Paused、Prepared、PlaybackCompleted状态进入到Stopped状态,一旦进入到Stopped状态,那么必须要调用prepare重新进入Prepared状态才能够重新进行播放;同样的,如果已经进入到Stopped状态,则再调用stop不会有任何影响;seekTo方法是异步的,调用完成后会有callback事件onSeekComplete上抛,seekTo可以在Started、Paused、Prepared、PlaybackCompleted状态下调用;当播放结束时,如果setLooping设置为true,那么播放器将会保持在Started状态,如果setLooping设置为false,那么播放器将会进入到PlaybackCompleted状态;在PlaybackCompleted状态下,调用start方法将会从头开始重新播放,并且进入到Started状态。 2、异常处理

从上面我们可以了解到,MediaPlayer维护了一套状态机,并且调用MediaPlayer方法时会检查当前调用是否是非法的,这套状态机机制在 mediaplayer.cpp 中。 MediaPlayer 定义了如下状态:

enum media_player_states { MEDIA_PLAYER_STATE_ERROR = 0, MEDIA_PLAYER_IDLE = 1 mOnCompletionInternalListener.onCompletion(mMediaPlayer); OnCompletionListener onCompletionListener = mOnCompletionListener; if (onCompletionListener != null && ! error_was_handled) { onCompletionListener.onCompletion(mMediaPlayer); } } stayAwake(false); return; 3、对new、release、reset的一些理解

相关代码路径:

MediaPlayer.javaandroid_media_MediaPlayer.cppmediaplayer.cpp 3.1、new

在看正式的 new MediaPlayer 流程前,我们先要注意MediaPlayer.java中的如下代码段:

static { System.loadLibrary("media_jni"); native_init(); }

它会加载media_jni.so,并执行native函数 android_media_MediaPlayer_native_init 。native_init会获取java类中的postEventFromNative方法ID,mNativeContext、mNativeSurfaceTexture等字段的ID,获取到的ID会存储在静态变量fields_t中,后期可以通过这些ID找到Java对象中对应的成员,获取其存储的值或者向其中存储值。

static fields_t fields; fields.context = env->GetFieldID(clazz, "mNativeContext", "J"); fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");

接下来看new MediaPlayer创建一个对象会做哪些事情:

private MediaPlayer(int sessionId) { ... try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) { native_setup(new WeakReference(this), attributionSourceState.getParcel()); } }

核心是调用native_setup方法,需要将自身的弱引用对象作为参数向下传递:

static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jAttributionSource) { Parcel* parcel = parcelForJavaObject(env, jAttributionSource); android::content::AttributionSourceState attributionSource; attributionSource.readFromParcel(parcel); // 创建MediaPlayer native对象 sp mp = sp::make(attributionSource); // 创建并注册Callback对象 sp listener = new JNIMediaPlayerListener(env, thiz, weak_this); mp->setListener(listener); // 将MediaPlayer对象存储到Java对象中 setMediaPlayer(env, thiz, mp); }

native_setup干了3件事:

创建MediaPlayer native对象;用传下来的弱引用对象创建Listener对象,用于Callback调用;将MediaPlayer native对象指针到Java对象中;

来看看如何存储MediaPlayer native指针的:

static sp setMediaPlayer(JNIEnv* env, jobject thiz, const sp& player) { Mutex::Autolock l(sLock); sp old = (MediaPlayer*)env->GetLongField(thiz, fields.context); // 手动增加强引用计数 if (player.get()) { player->incStrong((void*)setMediaPlayer); } // 检查mNativeContext中存储的MediaPlayer对象 if (old != 0) { old->decStrong((void*)setMediaPlayer); } // 将MediaPlayer的地址存储到mNativeContext env->SetLongField(thiz, fields.context, (jlong)player.get()); return old; }

这里有3个步骤:

首先要增加MediaPlayer native对象的强引用计数;检查mNativeContext中存储的地址是否为NULL,如果不为NULL则需要销毁存储的MediaPlayer对象;将新的MediaPlayer对象存储到mNativeContext中。

虽然我们会把MediaPlayer对象存储到Java字段中,但是其引用计数在setMediaPlayer中仍为1,如不增加引用计数,出了当前作用域native对象将自动销毁。

3.2、reset

reset的作用是重置当前MediaPlayer对象,使其恢复到new的状态。我们来看下reset实现:

status_t MediaPlayer::reset_l() { mLoop = false; if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR; // 1.阻止 MEDIA_PREPARED 事件上抛 mPrepareSync = false; if (mPlayer != 0) { // 2.调用内部实现的reset status_t ret = mPlayer->reset(); if (ret != NO_ERROR) { ALOGE("reset() failed with return code (%d)", ret); mCurrentState = MEDIA_PLAYER_STATE_ERROR; } else { // 2.调用内部实现的disconnect mPlayer->disconnect(); mCurrentState = MEDIA_PLAYER_IDLE; } // 3.销毁内部实现 mPlayer = 0; return ret; } // 4.清除设置 clear_l(); return NO_ERROR; }

reset主要会做3件事情:

如果当前状态是Preparing,那么会将 mPrepareSync 置为fasle,阻止 MEDIA_PREPARED 事件上抛,具体内部如何操作后续再研究;调用 MediaPlayer 内部实例的 disconnect 方法断开连接,如果处在播放状态将会停止播放(这点后续再研究);销毁 MediaPlayer 内部实例; 3.3、release

release会直接销毁掉MediaPlayer native对象,一旦调用release,当前MediaPlayer java对象将不能够再被使用:

MediaPlayer::~MediaPlayer() { ALOGV("destructor"); if (mAudioAttributesParcel != NULL) { delete mAudioAttributesParcel; mAudioAttributesParcel = NULL; } AudioSystem::releaseAudioSessionId(mAudioSessionId, (pid_t)-1); disconnect(); IPCThreadState::self()->flushCommands(); }

MediaPlayer 的析构函数同样也会调用内部实例的 disconnect 方法断开连接,之后会销毁 MediaPlayer 内部实例,这两点和reset是相同的。我们在想要结束activity,可以先调用reset,再调用release,也可以直接调用release来释放资源。

关注公众号《青山渺渺》阅读完整内容; 如有问题可在公众号后台私信,也可进入音视频开发技术分享群一起讨论!

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭